#include "abootloader.h"
#include "config.h"


#ifdef STM32F1

bool Bootloader::unlock_flash(void) {
    // 解锁FLASH
    FLASH->KEYR = FLASH_KEY1;
    FLASH->KEYR = FLASH_KEY2;
    return true;
}

bool Bootloader::lock_flash(void) {
    // 锁定FLASH
    FLASH->CR |= FLASH_CR_LOCK;
    return true;
}

bool Bootloader::erase_flash(uint32_t start, uint32_t end) {
    // 解锁FLASH
    this->unlock_flash();
    // 使用HAL库擦除FLASHstart到end的扇区
    FLASH_EraseInitTypeDef EraseInitStruct;
    EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
    EraseInitStruct.PageAddress = start;
    EraseInitStruct.NbPages = 1 + ((end - start) / FLASH_PAGE_SIZE);
    uint32_t PageError;
    HAL_FLASHEx_Erase(&EraseInitStruct, &PageError);
    // 锁定FLASH
    this->lock_flash();
    return false;
}

bool Bootloader::write_flash(uint32_t addr, uint16_t *data, uint32_t len) {
    // 解锁FLASH
    this->unlock_flash();
    // 写入FLASH
    FLASH->CR |= FLASH_CR_PG;
    for (uint32_t i = 0; i < len; i++) {
        *(__IO uint16_t *) (addr + i * 2) = data[i];
        while (FLASH->SR & FLASH_SR_BSY);
    }
    FLASH->CR &= ~FLASH_CR_PG;
    // 锁定FLASH
    this->lock_flash();
    return false;
}

bool Bootloader::jump_to_app(void) {
    EEPROM.write(0,0x55);
    Serial.println("Run...");
    delay(100);
    void (*appResetHandler)(void) = (void (*)(void)) (*(uint32_t *) (APP_START_ADDRESS + 4));
    __set_MSP(*(uint32_t *) APP_START_ADDRESS);
    // 设置堆栈指针
    // 关闭所有中断
    __disable_irq();
    // 关闭systick
    SysTick->CTRL = 0;
    // 关闭所有外设
    HAL_DeInit();
    // 跳转到app代码
    appResetHandler();
    return false;
}


bool Bootloader::read_flash(uint32_t addr, uint16_t *data, uint32_t len) {
    for (uint32_t i = 0; i < len; i++) {
        data[i] = *(__IO uint16_t *) (addr + i * 2);
    }
    return false;
}

Bootloader::Bootloader(HardwareSerial *serial, uint32_t app_addr) {
    this->app_addr = app_addr;
    this->serial = serial;
    this->serial->begin(115200);
    this->serial->setTimeout(1000);
}


bool Bootloader::poll(void) {
    // 每一包数据是32字节 算上帧头帧尾和校验码是48字节
    uint8_t error = 0;
    uint8_t cmd = 0;
    uint32_t addr = 0;
    uint16_t crc = 0;
    if (this->serial->available() >= 40) {
        this->serial->readBytes(this->rx_buffer, 40);
        // 帧头1个字节0x55,命令1个字节,地址4个字节,数据32个字节,校验2个字节
        if (this->rx_buffer[0] != 0x55) {
            error |= ERR_FRAME_HEADER;
        }
        // 计算从第0个字节到第38个字节的CRC16校验码
        crc = this->crc16(rx_buffer, 38);
        if (crc == (this->rx_buffer[38] | (this->rx_buffer[39] << 8))) {
            addr = (this->rx_buffer[5] << 24) | (this->rx_buffer[4] << 16) | (this->rx_buffer[3] << 8) |
                   this->rx_buffer[2];
            if (this->rx_buffer[1] == WRITE) {
                this->fill_buffer(this->rx_buffer + 6, addr);
            }
            if (this->rx_buffer[1] == RUN) {
                this->jump_to_app();
            }
            digitalWrite(PIN_RS485_EN, HIGH);
            this->serial->write(ACK);
            delayMicroseconds(100);
            digitalWrite(PIN_RS485_EN, LOW);
        } else {
            error |= ERR_CRC;
            digitalWrite(PIN_RS485_EN, HIGH);
            this->serial->write(NACK);
            delayMicroseconds(100);
            digitalWrite(PIN_RS485_EN, LOW);
        }
    }

    return false;
}

uint16_t Bootloader::crc16(uint8_t *data, uint8_t len) {
    uint16_t crc = 0xFFFF;
    for (uint8_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x01) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc = crc >> 1;
            }
        }
    }
    return crc;;
}

bool Bootloader::fill_buffer(uint8_t *data, uint32_t addr) {
    for (uint8_t i = 0; i < 16; i++) {
        this->fl_buffer[this->index] = (data[2 * i + 1] << 8) | (data[2 * i]);
        this->index++;
    }
    // 判断缓冲区是否已经满了
    if (this->index == 512) {
        digitalToggle(PIN_LED);
        this->index = 0;
        this->unlock_flash();
        this->erase_flash(addr, addr + 0x3FF);
        this->write_flash(addr, this->fl_buffer, 0x400);
        this->lock_flash();
        digitalWrite(PIN_RS485_EN, HIGH);
        this->serial->write(ACK);
        delayMicroseconds(100);
        digitalWrite(PIN_RS485_EN, LOW);
        this->serial->flush();
    }
    return false;
}

bool Bootloader::check_update_flag(void) {
    // 读取地址0x08003C00的数据
    uint16_t data[1];

    if(EEPROM.read(0)!=0x55){
        Serial.println("First Run...");
        return true;
    }
    this->read_flash(0x08003C00, data, 1);
    delay(100);

    if (data[0] == 0x5555) {
        return true;
    } else {
        return false;
    }

}


#endif